`timescale 1ns / 10ps
module barrel_shifter_tb ();

  reg  [31:0] dati;
  reg  [ 4:0] shamt;
  reg         shdir;
  reg         sharith;
  wire [31:0] dato;
  wire [31:0] dato_ref;
  wire        unmatch;

  parameter period = 10;

  initial begin : start_test
    integer i;
    shamt = 5'b0;
    dati = 32'b0;
    shdir = 1'b0;
    sharith = 1'b0;
    donnt_shift;
    #period;
    test_sll;
    test_srl;
    test_sra;
  end

  always begin : catch_unmatch
    wait (unmatch);
    #1;  // filter burr
    if (unmatch) begin
      $display("Found Unmatch");
      #period;
      $stop;
    end
  end

  initial begin
    $dumpfile("shifter_test.wave");
    $dumpvars;
    #(50 * period);
    $display("##########");
    $display("## PASS ##");
    $display("##########");
    $stop;
  end

  task test_sll;
    reg [30:0] rnd;
    reg [4:0] ramt;
    integer i;
    begin
      do_sll({$random}, 0);
      #period;
      do_sll(0, {$random});
      #period;
      for (i = 0; i < 5; i = i + 1) begin
        rnd = {$random};
        ramt = {$random};
        do_sll({1'b1, rnd}, ramt);
        #period;
        do_sll({1'b0, rnd}, ramt);
        #period;
      end
    end
  endtask

  task test_srl;
    reg [30:0] rnd;
    reg [4:0] ramt;
    integer i;
    begin
      do_srl({$random}, 0);
      #period;
      do_srl(0, {$random});
      #period;
      for (i = 0; i < 5; i = i + 1) begin
        rnd = {$random};
        ramt = {$random};
        do_srl({1'b1, rnd}, ramt);
        #period;
        do_srl({1'b0, rnd}, ramt);
        #period;
      end
    end
  endtask

  task test_sra;
    reg [30:0] rnd;
    reg [4:0] ramt;
    integer i;
    begin
      do_sra({$random}, 0);
      #period;
      do_sra(0, {$random});
      #period;
      for (i = 0; i < 5; i = i + 1) begin
        rnd = {$random};
        ramt = {$random};
        do_sra({1'b1, rnd}, ramt);
        #period;
        do_sra({1'b0, rnd}, ramt);
        #period;
      end
    end
  endtask

  task do_sll;
    input [31:0] din;
    input [5:0] amt;
    begin
      dati = din;
      shamt = amt;
      shdir = 1'b0;
      sharith = 1'b0;
    end
  endtask

  task do_srl;
    input [31:0] din;
    input [5:0] amt;
    begin
      dati = din;
      shamt = amt;
      shdir = 1'b1;
      sharith = 1'b0;
    end
  endtask

  task do_sra;
    input [31:0] din;
    input [5:0] amt;
    begin
      dati = din;
      shamt = amt;
      shdir = 1'b1;
      sharith = 1'b1;
    end
  endtask

  task donnt_shift;
    begin
      shamt   = 5'b0;
      shdir   = 1'b0;
      sharith = 1'b0;
    end
  endtask

  barrel_shifter #(
      .SHAMT_WIDTH(5)
  ) dut_inst (
      .dat_i(dati),
      .shamt_i(shamt),
      .shdir_i(shdir),
      .sharith_i(sharith),
      .dat_o(dato)
  );

  shifter_ref #(
      .SHAMT_WIDTH(5)
  ) refm_inst (
      .dat_i(dati),
      .shamt_i(shamt),
      .shdir_i(shdir),
      .sharith_i(sharith),
      .dat_o(dato_ref)
  );

  assign unmatch = |(dato ^ dato_ref);

endmodule
